# frozen_string_literal: true

require "cases/helper_sqlserver"
require "models/person"
require "models/reader"

class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
  fixtures :people, :readers

  before do
    Person.columns
    Reader.columns
  end

  it "uses with updlock by default" do
    assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)} do
      _(Person.lock(true).to_a).must_equal Person.all.to_a
    end
  end

  describe "For simple finds with default lock option" do
    it "lock with simple find" do
      assert_nothing_raised do
        Person.transaction do
          _(Person.lock(true).find(1)).must_equal Person.find(1)
        end
      end
    end

    it "lock with scoped find" do
      assert_nothing_raised do
        Person.transaction do
          Person.lock(true).scoping do
            _(Person.find(1)).must_equal Person.find(1)
          end
        end
      end
    end

    it "lock with eager find" do
      assert_nothing_raised do
        Person.transaction do
          person = Person.lock(true).includes(:readers).find(1)
          _(person).must_equal Person.find(1)
        end
      end
    end

    it "can add a custom lock directive" do
      assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)} do
        Person.lock("WITH(HOLDLOCK, ROWLOCK)").load
      end
    end

    describe "joining tables" do
      it "joined tables use updlock by default" do
        assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) INNER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do
          Person.lock(true).joins(:readers).load
        end
      end

      it "joined tables can use custom lock directive" do
        assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) INNER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do
          Person.lock("WITH(NOLOCK)").joins(:readers).load
        end
      end

      it "left joined tables use updlock by default" do
        assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do
          Person.lock(true).left_joins(:readers).load
        end
      end

      it "left joined tables can use custom lock directive" do
        assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) LEFT OUTER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do
          Person.lock("WITH(NOLOCK)").left_joins(:readers).load
        end
      end
    end
  end

  describe "For paginated finds" do
    before do
      Person.delete_all
      20.times { |n| Person.create!(first_name: "Thing_#{n}") }
    end

    it "copes with eager loading un-locked paginated" do
      eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/
      loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/

      assert_queries_match(/#{eager_ids_sql}|#{loader_sql}/, count: 2) do
        people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a
        _(people[0].first_name).must_equal "Thing_10"
        _(people[1].first_name).must_equal "Thing_11"
        _(people[2].first_name).must_equal "Thing_12"
        _(people[3].first_name).must_equal "Thing_13"
        _(people[4].first_name).must_equal "Thing_14"
      end
    end
  end
end
